/**
 * Copyright  2025, Hai Yue Xing He  ZHAO LIMIN
 *
 * @author        ZHAO LIMIN
 * @version       1.0.1
 * @since         2025-04
 * public static ts   A 2D Cartesian point.
 */
import { Check } from './Check'
import { defined } from './Defined'
import { DeveloperError } from './DeveloperError'
import { HYXHMathUtil } from './HYXHMathUtil'
import { defaultValue } from './HYXHDefaultValue'
import { IPackable, packs, unpacks, packArray, unpackArray } from './Packable'
import { UNION_NUMBER_ARRAY } from './HDataType'
import { ICartesian } from './Cartesian'
import { IArrayIndex, INumberArray } from './HXArray'

interface ICartesian2D extends ICartesian {
  x: number
  y: number
}

export class Cartesian2 implements ICartesian2D, IPackable, INumberArray, IArrayIndex {
  [index: number]: number
  
  public get x(): number {
    return this[0]
  }
  public set x(v:number) {
    this[0] = v??0.0
  }
  //public y: number
  public get y(): number {
    return this[1]
  }
  public set y(v:number) {
    this[1] = v??0.0
  }
  
  public get length(): number {
    return Cartesian2.packedLength  
  }
  
  /**
   * A 2D Cartesian point.
   * @alias Cartesian2
   * @constructor
   *
   * @param {number} [x=0.0] The X component.
   * @param {number} [y=0.0] The Y component.
   *
   * @see Cartesian3
   * @see Cartesian4
   * @see Packable
   */
  constructor(x?: number, y?: number) {
    /**
     * The X component.
     * @type {number}
     * @default 0.0
     */
    this.x = x ?? 0.0

    /**
     * The Y component.
     * @type {number}
     * @default 0.0
     */
    this.y = y ?? 0.0
  }
  
  push(...items: number[]): number[] {
    this[0] = items[0]
    this[1] = items[1]
    return [this[0], this[1]]    
  }
  /**
   * Creates a Cartesian2 instance from x and y coordinates.
   *
   * @param {number} x The x coordinate.
   * @param {number} y The y coordinate.
   * @param {Cartesian2} [result] The object onto which to store the result.
   * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided.
   */
  public static fromElements(x: number, y: number, result?: ICartesian): ICartesian | undefined {
    if (!defined(result)) {
      return new Cartesian2(x, y)
    }
    result!.x = x
    result!.y = y
    return result
  }
  /**
   * Duplicates a Cartesian2 instance.
   *
   * @param {Cartesian2} cartesian The Cartesian to duplicate.
   * @param {Cartesian2} [result] The object onto which to store the result.
   * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. (Returns undefined if cartesian is undefined)
   */
  public static clone(cartesian: ICartesian, result?: ICartesian): ICartesian | undefined {
    if (!defined(cartesian)) {
      return undefined
    }
    if (!defined(result)) {
      return new Cartesian2(cartesian.x, cartesian.y)
    }
    result!.x = cartesian.x
    result!.y = cartesian.y
    return result
  }

  /**
   * Creates a Cartesian2 instance from an existing Cartesian3.  This simply takes the
   * x and y properties of the Cartesian3 and drops z.
   * @function
   *
   * @param {Cartesian2} cartesian The Cartesian3 instance to create a Cartesian2 instance from.
   * @param {Cartesian2} [result] The object onto which to store the result.
   * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided.
   */
  public static readonly fromCartesian3 = Cartesian2.clone

  /**
   * Creates a Cartesian2 instance from an existing Cartesian4.  This simply takes the
   * x and y properties of the Cartesian4 and drops z and w.
   * @function
   *
   * @param {Cartesian4} cartesian The Cartesian4 instance to create a Cartesian2 instance from.
   * @param {Cartesian2} [result] The object onto which to store the result.
   * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided.
   */
  public static readonly fromCartesian4 = Cartesian2.clone

  /**
   * The number of elements used to pack the object into an array.
   * @type {number}
   */
  public static readonly packedLength = 2

  /**
   * @override IPackable
   * @returns
   * @see Packable.IPackable
   */
  public getPackedLength(): number {
    return Cartesian2.packedLength
  }
  public getRowLength(): number {
    return 1
  }
  public getColumnLength(): number {
    return Cartesian2.packedLength
  }
  /**
   * Stores the provided instance into the provided array.
   *
   * @param {Cartesian2} value The value to pack.
   * @param {number[]} array The array to pack into.
   * @param {number} [startingIndex=0] The index into the array at which to start packing the elements.
   *
   * @returns {number[]} The array that was packed into
   */
  public static pack(
    value: ICartesian,
    array: UNION_NUMBER_ARRAY,
    startingIndex: number = 0,
  ): UNION_NUMBER_ARRAY {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('value', value)
    Check.defined('array', array)
    Check.typeOf.tnumber.checkInteger('startingIndex', startingIndex)
    //>>includeEnd('debug');
    startingIndex = startingIndex ?? 0
    packs(value as Cartesian2, array, startingIndex)
    return array
  }
  /**
   * Stores the provided instance into the provided array.
   *
   * @param {number[]} array The array to pack into.
   * @param {number} [startingIndex=0] The index into the array at which to start packing the elements.
   *
   */
  public pack(array: UNION_NUMBER_ARRAY, startingIndex: number = 0) {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('array', array)
    Check.defined('array', array)
    Check.typeOf.tnumber.checkInteger('startingIndex', startingIndex)
    //>>includeEnd('debug');
    startingIndex = startingIndex ?? 0
    array[startingIndex++] = this.x
    array[startingIndex] = this.y
    return array
  }
  /**
   * Retrieves an instance from a packed array.
   *
   * @param {number[]} array The packed array.
   * @param {number} [startingIndex=0] The starting index of the element to be unpacked.
   * @param {Cartesian2} [result] The object into which to store the result.
   * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided.
   */
  public static unpack(
    array: UNION_NUMBER_ARRAY,
    startingIndex: number,
    result?: ICartesian,
  ): ICartesian | undefined {
    //>>includeStart('debug', pragmas.debug);
    Check.defined('array', array)
    //>>includeEnd('debug');

    startingIndex = startingIndex ?? 0

    if (!defined(result)) {
      result = new Cartesian2()
    }
    result = unpacks(array, startingIndex, result as Cartesian2 ) as Cartesian2
    return result
  }
  /**
   * Retrieves an instance from a packed array.
   *
   * @param {number[]} array The packed array.
   * @param {number} [startingIndex=0] The starting index of the element to be unpacked.
   */
  public unpack(array: UNION_NUMBER_ARRAY, startingIndex: number = 0) {
    //>>includeStart('debug', pragmas.debug);
    Check.defined('array', array)
    //>>includeEnd('debug');
    startingIndex = startingIndex ?? 0
    this.x = array[startingIndex++]
    this.y = array[startingIndex]
  }
  /**
   * 将一组Cartesian2轴展平为一组组件。
   *
   * @param {Cartesian2[]} array The array of cartesians to pack.
   * @param {number[]} [result] The array onto which to store the result. If this is a typed array, it must have array.length * 2 components, else a {@link DeveloperError} will be thrown. If it is a regular array, it will be resized to have (array.length * 2) elements.
   * @returns {number[]} The packed array.
   */
  public static packArray(array: Array<ICartesian>, result?: UNION_NUMBER_ARRAY): UNION_NUMBER_ARRAY {
    //>>includeStart('debug', pragmas.debug);
    Check.defined('array', array)
    //>>includeEnd('debug');
    if (!defined(result)) {
      result = new Array(array.length * Cartesian2.packedLength) 
    }
    packArray(array as Cartesian2[], result)
    return result
  }
  /**
   * 将一维笛卡尔分量数组解包为一维Cartesian2分量数组。
   *
   * @param {number[]} array The array of components to unpack.
   * @param {Cartesian2[]} [result] The array onto which to store the result.
   * @returns {Cartesian2[]} The unpacked array.
   */
  public static unpackArray(array: UNION_NUMBER_ARRAY, result: Array<ICartesian>): Array<ICartesian> {
    //>>includeStart('debug', pragmas.debug);
    Check.defined('array', array)
    Check.typeOf.tnumber.greaterThanOrEquals('array.length', array.length, Cartesian2.packedLength)
    if (array.length % Cartesian2.packedLength !== 0) {
      throw new DeveloperError('array length must be a multiple of 2.')
    }
    //>>includeEnd('debug');
    if (!defined(result)) {
      result = new Array(length / Cartesian2.packedLength)
      for (let i = 0; i < result.length; ++i) {
        result[i] = new Cartesian2()
      }
    } else {
      result.length = length / Cartesian2.packedLength
    }
    unpackArray(array, result as Cartesian2[])
    return result
  }
  /**
   * Creates a Cartesian2 from two consecutive elements in an array.
   * @function
   *
   * @param {number[]} array The array whose two consecutive elements correspond to the x and y components, respectively.
   * @param {number} [startingIndex=0] The offset into the array of the first element, which corresponds to the x component.
   * @param {Cartesian2} [result] The object onto which to store the result.
   * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided.
   *
   * @example
   * // Create a Cartesian2 with (1.0, 2.0)
   * const v = [1.0, 2.0];
   * const p = public static fromArray(v);
   *
   * // Create a Cartesian2 with (1.0, 2.0) using an offset into an array
   * const v2 = [0.0, 0.0, 1.0, 2.0];
   * const p2 = public static fromArray(v2, 2);
   */
  public static readonly fromArray = Cartesian2.unpack // Cartesian2.unpack

  /**
   * Computes the value of the maximum component for the supplied Cartesian.
   *
   * @param {Cartesian2} cartesian The cartesian to use.
   * @returns {number} The value of the maximum component.
   */
  public static maximumComponent(cartesian: ICartesian): number {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('cartesian', cartesian)
    //>>includeEnd('debug');

    return Math.max(cartesian.x, cartesian.y)
  }
  /**
   * Computes the value of the minimum component for the supplied Cartesian.
   *
   * @param {Cartesian2} cartesian The cartesian to use.
   * @returns {number} The value of the minimum component.
   */
  public static minimumComponent(cartesian: ICartesian): number {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('cartesian', cartesian)
    //>>includeEnd('debug');

    return Math.min(cartesian.x, cartesian.y)
  }
  /**
   * Compares two Cartesians and computes a Cartesian which contains the minimum components of the supplied Cartesians.
   *
   * @param {Cartesian2} first A cartesian to compare.
   * @param {Cartesian2} second A cartesian to compare.
   * @param {Cartesian2} result The object into which to store the result.
   * @returns {Cartesian2} A cartesian with the minimum components.
   */
  public static minimumByComponent(
    first: ICartesian,
    second: ICartesian,
    result?: ICartesian,
  ): ICartesian | undefined {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('first', first)
    Check.typeOf.object('second', second)
    Check.typeOf.object('result', result)
    //>>includeEnd('debug');
    if (!defined(result)) {
      result = new Cartesian2()
    }
    result!.x = Math.min(first.x, second.x)
    result!.y = Math.min(first.y, second.y)
    return result
  }
  /**
   * Compares two Cartesians and computes a Cartesian which contains the maximum components of the supplied Cartesians.
   *
   * @param {Cartesian2} first A cartesian to compare.
   * @param {Cartesian2} second A cartesian to compare.
   * @param {Cartesian2} result The object into which to store the result.
   * @returns {Cartesian2} A cartesian with the maximum components.
   */
  public static maximumByComponent(
    first: ICartesian,
    second: ICartesian,
    result?: ICartesian,
  ): ICartesian | undefined {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('first', first)
    Check.typeOf.object('second', second)
    Check.typeOf.object('result', result)
    //>>includeEnd('debug');
    if (!defined(result)) {
        result = new Cartesian2()
    }
    result!.x = Math.max(first.x, second.x)
    result!.y = Math.max(first.y, second.y)
    return result
  }
  /**
   * Constrain a value to lie between two values.
   *
   * @param {Cartesian2} value The value to clamp.
   * @param {Cartesian2} min The minimum bound.
   * @param {Cartesian2} max The maximum bound.
   * @param {Cartesian2} result The object into which to store the result.
   * @returns {Cartesian2} The clamped value such that min <= result <= max.
   */
  public static clamp(
    value: ICartesian,
    min: ICartesian,
    max: ICartesian,
    result?: ICartesian,
  ): ICartesian | undefined {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('value', value)
    Check.typeOf.object('min', min)
    Check.typeOf.object('max', max)
    Check.typeOf.object('result', result)
    //>>includeEnd('debug');
    const x = HYXHMathUtil.clamp(value.x, min.x, max.x)
    const y = HYXHMathUtil.clamp(value.y, min.y, max.y)
    if (!defined(result)) {
        result = new Cartesian2()
    }
    result!.x = x
    result!.y = y
    return result
  }
  /**
   * Computes the provided Cartesian's squared magnitude.
   *
   * @param {Cartesian2} cartesian The Cartesian instance whose squared magnitude is to be computed.
   * @returns {number} The squared magnitude.
   */
  public static magnitudeSquared(cartesian: ICartesian): number {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('cartesian', cartesian)
    //>>includeEnd('debug');
    return cartesian.x * cartesian.x + cartesian.y * cartesian.y
  }
  /**
   * Computes the Cartesian's magnitude (length).
   *
   * @param {Cartesian2} cartesian The Cartesian instance whose magnitude is to be computed.
   * @returns {number} The magnitude.
   */
  public static magnitude(cartesian: ICartesian): number {
    return Math.sqrt(Cartesian2.magnitudeSquared(cartesian))
  }
  public static readonly distanceScratch = new Cartesian2()
  /**
   * Computes the distance between two points.
   *
   * @param {Cartesian2} left The first point to compute the distance from.
   * @param {Cartesian2} right The second point to compute the distance to.
   * @returns {number} The distance between two points.
   *
   * @example
   * // Returns 1.0
   * const d = public static distance(new Cartesian2(1.0, 0.0), new Cartesian2(2.0, 0.0));
   */
  public static distance(left: ICartesian, right: ICartesian): number {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('left', left)
    Check.typeOf.object('right', right)
    //>>includeEnd('debug');    
    Cartesian2.subtract(left, right, Cartesian2.distanceScratch)
    return Cartesian2.magnitude(Cartesian2.distanceScratch)
  }

  /**
   * Computes the squared distance between two points.  Comparing squared distances
   * using this function is more efficient than comparing distances using {@link Cartesian2#distance}.
   *
   * @param {Cartesian2} left The first point to compute the distance from.
   * @param {Cartesian2} right The second point to compute the distance to.
   * @returns {number} The distance between two points.
   *
   * @example
   * // Returns 4.0, not 2.0
   * const d = public static distance(new Cartesian2(1.0, 0.0), new Cartesian2(3.0, 0.0));
   */
  public static distanceSquared(left: ICartesian, right: ICartesian): number {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('left', left)
    Check.typeOf.object('right', right)
    //>>includeEnd('debug');
    Cartesian2.subtract(left, right, Cartesian2.distanceScratch)
    return Cartesian2.magnitudeSquared(Cartesian2.distanceScratch)
  }
  /**
   * Computes the normalized form of the supplied Cartesian.
   *
   * @param {Cartesian2} result The object onto which to store the result.
   * @param {Cartesian2} cartesian The Cartesian to be normalized.
   * @returns {Cartesian2} The modified result parameter.
   */
  public static normalize(result: ICartesian, cartesian: ICartesian): ICartesian | undefined {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('result', result)
    Check.typeOf.object('cartesian', cartesian)
    //>>includeEnd('debug');
    const magnitude = Cartesian2.magnitude(cartesian)
    result.x = cartesian.x / magnitude
    result.y = cartesian.y / magnitude
    //>>includeStart('debug', pragmas.debug);
    //if (isNaN(result.x) || isNaN(result.y)) {
    //  throw new DeveloperError("normalized result is not a number");
    //}
    //>>includeEnd('debug');
    if (!defined(result)) {
        result = new Cartesian2()
    }
    return result
  }
  /**
   * 计算两个笛卡尔坐标系的点积（标量积）。
   *
   * @param {Cartesian2} left The first Cartesian.
   * @param {Cartesian2} right The second Cartesian.
   * @returns {number} The dot product.
   */
  public static dot(left: ICartesian, right: ICartesian): number {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('left', left)
    Check.typeOf.object('right', right)
    //>>includeEnd('debug');
    return left.x * right.x + left.y * right.y
  }
  /**
   * 计算隐式将输入向量的Z坐标设置为0所产生的交叉积的大小
   *
   * @param {Cartesian2} left The first Cartesian.
   * @param {Cartesian2} right The second Cartesian.
   * @returns {number} The cross product.
   */
  public static cross(left: ICartesian, right: ICartesian): number {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('left', left)
    Check.typeOf.object('right', right)
    //>>includeEnd('debug');
    return left.x * right.y - left.y * right.x
  }
  /**
   * 计算两个笛卡尔坐标系的分量乘积。
   *
   * @param {Cartesian2} left The first Cartesian.
   * @param {Cartesian2} right The second Cartesian.
   * @param {Cartesian2} result The object onto which to store the result.
   * @returns {Cartesian2} The modified result parameter.
   */
  public static multiplyComponents(
    left: ICartesian,
    right: ICartesian,
    result?: ICartesian,
  ): ICartesian | undefined {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('left', left)
    Check.typeOf.object('right', right)
    Check.typeOf.object('result', result)
    //>>includeEnd('debug');
    if (!defined(result)) {
        result = new Cartesian2()
    }
    result!.x = left.x * right.x
    result!.y = left.y * right.y
    return result
  }
  /**
   * 计算两个笛卡尔坐标系的分量商。
   *
   * @param {Cartesian2} left The first Cartesian.
   * @param {Cartesian2} right The second Cartesian.
   * @param {Cartesian2} result The object onto which to store the result.
   * @returns {Cartesian2} The modified result parameter.
   */
  public static divideComponents(
    left: ICartesian,
    right: ICartesian,
    result?: ICartesian,
  ): ICartesian | undefined {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('left', left)
    Check.typeOf.object('right', right)
    Check.typeOf.object('result', result)
    //>>includeEnd('debug');
    if (!defined(result)) {
        result = new Cartesian2()
    }
    result!.x = left.x / right.x
    result!.y = left.y / right.y
    return result
  }
  /**
   * 计算两个笛卡尔坐标系的分量和。
   *
   * @param {Cartesian2} left The first Cartesian.
   * @param {Cartesian2} right The second Cartesian.
   * @param {Cartesian2} result The object onto which to store the result.
   * @returns {Cartesian2} The modified result parameter.
   */
  public static add(left: ICartesian, right: ICartesian,
    result?: ICartesian): ICartesian | undefined{
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('left', left)
    Check.typeOf.object('right', right)
    Check.typeOf.object('result', result)
    //>>includeEnd('debug');
    if (!defined(result)) {
        result = new Cartesian2()
    }    
    result!.x = left.x + right.x
    result!.y = left.y + right.y
    return result
  }
  /**
   * 计算两个笛卡尔坐标系的分量差。
   *
   * @param {Cartesian2} left The first Cartesian.
   * @param {Cartesian2} right The second Cartesian.
   * @param {Cartesian2} result The object onto which to store the result.
   * @returns {Cartesian2} The modified result parameter.
   */
  public static subtract(
    left: ICartesian, 
    right: ICartesian,
    result?: ICartesian): ICartesian | undefined {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('left', left)
    Check.typeOf.object('right', right)
    Check.typeOf.object('result', result)
    //>>includeEnd('debug');
    if ( !defined(result)) {
      result = new Cartesian2()
    }
    result!.x = left.x - right.x
    result!.y = left.y - right.y
    return result
  }
  /**
   * 将提供的笛卡尔分量与提供的标量逐个相乘。
   *
   * @param {Cartesian2} cartesian The Cartesian to be scaled.
   * @param {number} scalar The scalar to multiply with.
   * @param {Cartesian2} result The object onto which to store the result.
   * @returns {Cartesian2} The modified result parameter.
   */
  public static multiplyByScalar(
    cartesian: ICartesian,
    scalar: number,
    result?: ICartesian,
  ): ICartesian | undefined {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('cartesian', cartesian)
    Check.typeOf.number('scalar', scalar)
    Check.typeOf.object('result', result)
    //>>includeEnd('debug');
    if (!defined(result)) {
        result = new Cartesian2()
    }
    result!.x = cartesian.x * scalar
    result!.y = cartesian.y * scalar
    return result
  }
  /**
   * Divides the provided Cartesian componentwise by the provided scalar.
   *
   * @param {Cartesian2} cartesian The Cartesian to be divided.
   * @param {number} scalar The scalar to divide by.
   * @param {Cartesian2} result The object onto which to store the result.
   * @returns {Cartesian2} The modified result parameter.
   */
  public static divideByScalar(
    cartesian: Cartesian2,
    scalar: number,
    result?: ICartesian,
  ): ICartesian | undefined {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('cartesian', cartesian)
    Check.typeOf.number('scalar', scalar)
    Check.typeOf.object('result', result)
    //>>includeEnd('debug');
    if (!defined(result)) {
        result = new Cartesian2()
    }
    result!.x = cartesian.x / scalar
    result!.y = cartesian.y / scalar
    return result
  }
  /**
   * 否定了提供的笛卡尔坐标系。(反向/负向 赋值)
   *
   * @param {Cartesian2} cartesian The Cartesian to be negated.
   * @param {Cartesian2} result The object onto which to store the result.
   * @returns {Cartesian2} The modified result parameter.
   */
  public static negate(cartesian: ICartesian, result?: ICartesian): ICartesian | undefined {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('cartesian', cartesian)
    Check.typeOf.object('result', result)
    //>>includeEnd('debug');
    if (!defined(result)) {
      result = new Cartesian2()
    }
    result!.x = -cartesian.x
    result!.y = -cartesian.y
    return result
  }
  /**
   * 计算提供的笛卡尔坐标的绝对值。
   *
   * @param {Cartesian2} cartesian The Cartesian whose absolute value is to be computed.
   * @param {Cartesian2} result The object onto which to store the result.
   * @returns {Cartesian2} The modified result parameter.
   */
  public static abs(
    cartesian: ICartesian, result?: ICartesian): ICartesian | undefined {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('cartesian', cartesian)
    Check.typeOf.object('result', result)
    //>>includeEnd('debug');
    if (!defined(result)) {
        result = new Cartesian2()
    }
    result!.x = Math.abs(cartesian.x)
    result!.y = Math.abs(cartesian.y)
    return result
  }

  public static readonly lerpScratch = new Cartesian2()
  /**
   * 使用提供的笛卡尔坐标计算t处的线性插值或外推。
   *
   * @param {Cartesian2} start The value corresponding to t at 0.0.
   * @param {Cartesian2} end The value corresponding to t at 1.0.
   * @param {number} t The point along t at which to interpolate.
   * @param {Cartesian2} result The object onto which to store the result.
   * @returns {Cartesian2} The modified result parameter.
   */
  public static lerp(
    start: ICartesian,
    end: ICartesian,
    t: number,
    result?: ICartesian,
  ): ICartesian | undefined {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('start', start)
    Check.typeOf.object('end', end)
    Check.typeOf.number('t', t)
    Check.typeOf.object('result', result)
    //>>includeEnd('debug');
    if (!defined(result)) {
        result = new Cartesian2()
    }
    Cartesian2.multiplyByScalar(end, t, Cartesian2.lerpScratch)
    result = Cartesian2.multiplyByScalar(start, 1.0 - t, result)
    return Cartesian2.add(Cartesian2.lerpScratch, result!, result)
  }

  public static readonly angleBetweenScratch = new Cartesian2()
  public static readonly angleBetweenScratch2 = new Cartesian2()
  /**
   *
   * 返回提供的笛卡尔坐标之间的角度，单位为弧度。
   *
   * @param {Cartesian2} left The first Cartesian.
   * @param {Cartesian2} right The second Cartesian.
   * @returns {number} The angle between the Cartesians.
   */
  public static angleBetween(left: ICartesian, right: ICartesian): number {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('left', left)
    Check.typeOf.object('right', right)
    //>>includeEnd('debug');
    Cartesian2.normalize(left, Cartesian2.angleBetweenScratch)
    Cartesian2.normalize(right, Cartesian2.angleBetweenScratch2)
    return HYXHMathUtil.acosClamped(
      Cartesian2.dot(Cartesian2.angleBetweenScratch, Cartesian2.angleBetweenScratch2),
    )
  }
  public static readonly mostOrthogonalAxisScratch = new Cartesian2()
  /**
   * 返回与提供的笛卡尔坐标系最正交的轴。
   *
   * @param {Cartesian2} cartesian The Cartesian on which to find the most orthogonal axis.
   * @param {Cartesian2} result The object onto which to store the result.
   * @returns {Cartesian2} The most orthogonal axis.
   */
  public static mostOrthogonalAxis(
    cartesian: ICartesian,
    result?: ICartesian,
  ): ICartesian | undefined {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('cartesian', cartesian)
    Check.typeOf.object('result', result)
    //>>includeEnd('debug');
    if (!defined(result)) {
        result = new Cartesian2()
    }
    const f = Cartesian2.normalize(cartesian, Cartesian2.mostOrthogonalAxisScratch)
    Cartesian2.abs(f!, f)

    if (!defined(result)) {
      result = new Cartesian2()
    }

    if (f!.x <= f!.y) {
      result = defaultValue<Cartesian2>(
        Cartesian2.clone(Cartesian2.UNIT_X, result) as Cartesian2,
        new Cartesian2(),
      )
    } else {
      result = defaultValue<Cartesian2>(
        Cartesian2.clone(Cartesian2.UNIT_Y, result) as Cartesian2,
        new Cartesian2(),
      )
      //result = Cartesian2.clone(Cartesian2.UNIT_Y, result)
    }
    return result
  }
  /**
   * 按分量比较提供的Cartesian坐标，并返回
   * <code>true</code> if they are equal, <code>false</code> otherwise.
   *
   * @param {Cartesian2} [left] The first Cartesian.
   * @param {Cartesian2} [right] The second Cartesian.
   * @returns {boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise.
   */
  public static equals(left: ICartesian, right: ICartesian): boolean {
    return (
      left === right ||
      (defined(left) && defined(right) && left.x === right.x && left.y === right.y)
    )
  }
  /**
   * @private
   */
  public static equalsArray(cartesian: Cartesian2, array: Array<number>, offset: number): boolean {
    return cartesian.x === array[offset] && cartesian.y === array[offset + 1]
  }
  /**
   * 按分量比较提供的Cartesian坐标，并返回
   * <code>true</code> if they pass an absolute or relative tolerance test,
   * <code>false</code> otherwise.
   *
   * @param {Cartesian2} [left] The first Cartesian.
   * @param {Cartesian2} [right] The second Cartesian.
   * @param {number} [relativeEpsilon=0] The relative epsilon tolerance to use for equality testing.
   * @param {number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing.
   * @returns {boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise.
   */
  public static equalsEpsilon(
    left: ICartesian,
    right: ICartesian,
    relativeEpsilon: number,
    absoluteEpsilon: number = relativeEpsilon,
  ): boolean {
    return (
      left === right ||
      (defined(left) &&
        defined(right) &&
        HYXHMathUtil.equalsEpsilon(left.x, right.x, relativeEpsilon, absoluteEpsilon) &&
        HYXHMathUtil.equalsEpsilon(left.y, right.y, relativeEpsilon, absoluteEpsilon))
    )
  }
  /**
   * An immutable Cartesian2 instance initialized to (0.0, 0.0).
   *
   * @type {Cartesian2}
   * @constant
   */
  public static readonly ZERO = Object.freeze(new Cartesian2(0.0, 0.0))

  /**
   * An immutable Cartesian2 instance initialized to (1.0, 1.0).
   *
   * @type {Cartesian2}
   * @constant
   */
  public static readonly ONE = Object.freeze(new Cartesian2(1.0, 1.0))

  /**
   * An immutable Cartesian2 instance initialized to (1.0, 0.0).
   *
   * @type {Cartesian2}
   * @constant
   */
  public static readonly UNIT_X = Object.freeze(new Cartesian2(1.0, 0.0))

  /**
   * An immutable Cartesian2 instance initialized to (0.0, 1.0).
   *
   * @type {Cartesian2}
   * @constant
   */
  public static readonly UNIT_Y = Object.freeze(new Cartesian2(0.0, 1.0))

  /**
   * Duplicates this Cartesian2 instance.
   *
   * @param {Cartesian2} [result] The object onto which to store the result.
   * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided.
   */
  public clone(result?: ICartesian): ICartesian | undefined {
    return Cartesian2.clone(this, result)
  }

  /**
   * Compares this Cartesian against the provided Cartesian componentwise and returns
   * <code>true</code> if they are equal, <code>false</code> otherwise.
   *
   * @param {Cartesian2} [right] The right hand side Cartesian.
   * @returns {boolean} <code>true</code> if they are equal, <code>false</code> otherwise.
   */
  public equals(right: ICartesian): boolean {
    return Cartesian2.equals(this, right)
  }

  /**
   * Compares this Cartesian against the provided Cartesian componentwise and returns
   * <code>true</code> if they pass an absolute or relative tolerance test,
   * <code>false</code> otherwise.
   *
   * @param {Cartesian2} [right] The right hand side Cartesian.
   * @param {number} [relativeEpsilon=0] The relative epsilon tolerance to use for equality testing.
   * @param {number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing.
   * @returns {boolean} <code>true</code> if they are within the provided epsilon, <code>false</code> otherwise.
   */
  public equalsEpsilon(right: ICartesian, relativeEpsilon: number, absoluteEpsilon: number): boolean {
    return Cartesian2.equalsEpsilon(this, right, relativeEpsilon, absoluteEpsilon)
  }

  /**
   * Creates a string representing this Cartesian in the format '(x, y)'.
   *
   * @returns {string} A string representing the provided Cartesian in the format '(x, y)'.
   */
  public toString(): string {
    return `(${this.x}, ${this.y})`
  }
} // class
